function [SFDA SFDA_ST SFDA_BT ATA ATA_ST ATA_BT meanStability] = compareData(GTFilename, DATFilename, verbose, modif)
%Goal : Compare XML from ground truth and DATManager
addpath('.\xml_toolbox','-begin') ;
addpath('.\xml_tools','-begin') ;

if(nargin<3)
    verbose = 0;
end
if(nargin<4)
    modif = 0;
end

%DETECTION
%---------

%Step 1: Open both files and parse them (xml_load or xml_parse ?)
%Step 2: Match DATObject with DATManager and from objectfinder (ground truth)
%a) Calculate common area (c)
%b) Calculate common area ratio (a = c/GroundTruthArea)
%c) Calculate scale ratio (s = DATManager/GroundTruth)
%d) Calculate scale ratio weight (w = UAG(s,u,sig,r), u = 1, sig=0.25 ?, r = 1/sig^2(4) ?)
%e) Calculate "hit rate" (h% = w*a)
%f) Calculate Overlap (Overlap = |DATManager INTERSECTION GroundTruth| /
%|DATManager UNION GroundTruth|)
%Step 3: Give type --> Hit, NearHit, FalseAlarm, Misclassified, Missed
%Step 4: Check if Multihit
%Step 5: Show result matey ! (FDA, SFDA, FDA_T (threshold))

%load file, then parseany all line...
%DATXMLFile = xmlread('E:\Videos\RAW\a21p2s13v06T001C1_6940_7940.avi.xml');

%DATXMLFile = xmlread('D:/Usagers/matre697/Data.nobkp/Benchmark_2.avi.xml');
%GTXMLFile = xmlread('D:/Usagers/matre697/Data.nobkp/objectfinder/Benchmark_2.avi_GT.xml');
%GTXMLFile = xmlread('E:/Videos.nobkp/RAW/results/xml/GT/a21p2s13v06T001C1_6940_7940_b18_c106.avi_radialDistCorr0.0000014.avi_GT.xml');
%DATXMLFile = xmlread('E:/Videos.nobkp/RAW/results/xml/SIFT/a21p2s13v06T001C1_6940_7940_b18_c106.avi_radialDistCorr0.0000014.avi_TestTrackerCompare.xml');

fprintf('Now serving clients: %s and %s .... \n',GTFilename, DATFilename);

GTXMLFile = xmlread(GTFilename);
DATXMLFile = xmlread(DATFilename);

minDim = [22 18;36 12]; %22x18 or 30x12
sigma = 1/sqrt(2); %?

FDATreshold = 0.3;
TDAThreshold = 0.3;

% Find a deep list of all Frame elements.
allDATFrames = DATXMLFile.getElementsByTagName('Frame');
allGTFrames = GTXMLFile.getElementsByTagName('Frame');

% Note that the item list index is zero-based.
NFrames = allDATFrames.getLength-1;
if(NFrames ~= allGTFrames.getLength-1)
   fprintf('Error, files dont have the same numbers of frames.\n');
   return
end

FDA = zeros(NFrames+1,1);     %No threshold
FDA_ST = zeros(NFrames+1,1);  %Soft threshold
FDA_BT = zeros(NFrames+1,1);  %Binary threshold

allFrames = [];
mappedObjects = [];    %struct mappedObject : objectIdx == GTIdx (in case of falseAlarm addMaxGTIdx and put true in tag FA), array of match/miss/falseAlarm
offset = 1;
maxObjectGTIdx = 0;
maxObjectDATIdx = 0;

nbFalseAlarms = 0;
nbMatches = 0;
nbMisses = 0;
gotFirst = 0;
gotSecond = 0;

for k = NFrames:-1:NFrames-50
    GTFrame = allGTFrames.item(k);
	GTChildNode = GTFrame.getFirstChild;

    DATFrame = allDATFrames.item(k);
	DATChildNode = DATFrame.getFirstChild;
    
    %GT lookup
    GTObjects = lookupChild(GTChildNode);
    if(~isempty(GTObjects))
        for i=1:length(GTObjects)
            if(str2num(GTObjects(i).ID)>maxObjectGTIdx)
                maxObjectGTIdx = str2num(GTObjects(i).ID);
                gotFirst = 1;
            end
        end         
    end
    %DAT lookup
    DATObjects = lookupChild(DATChildNode);
    if(~isempty(DATObjects))
        for i=1:length(DATObjects)
            if(str2num(DATObjects(i).ID)>maxObjectDATIdx)
                maxObjectDATIdx = str2num(DATObjects(i).ID);
                gotSecond = 1;
            end
        end
    end
%     if gotFirst == 1 && gotSecond == 1
%         break;
%     end
end


objectAssociations = zeros(maxObjectGTIdx+2,maxObjectDATIdx+2);


for k = 0:NFrames
	%Check if there's any DATObject in this frame.
    DATFrame = allDATFrames.item(k);
	DATChildNode = DATFrame.getFirstChild;
	%Check if there's any GTObject in this frame.
    GTFrame = allGTFrames.item(k);
	GTChildNode = GTFrame.getFirstChild;
    
    %DAT lookup
    DATObjects = lookupChild(DATChildNode);
    
    %GT lookup
    GTObjects = lookupChild(GTChildNode);



    %Check if we can compare GTObjects with DATObjects
	if ~isempty([DATObjects GTObjects])
        allMatches = [];
        allMisses =[];
        allFalseAlarms =[];
        
        listToRejectGT = [];
        listToRejectDAT = [];
        %Discard too small objects
        for i=1:length(GTObjects)     
            ok = 0;
            for j = 1:length(minDim)
                ok = ok + checkSize(minDim(j,:),GTObjects(i).size);
            end
            if(ok==0)
                listToRejectGT = [listToRejectGT i];
                continue;
            end
        end
        for i=1:length(DATObjects)
            ok = 0;
            for j = 1:length(minDim)
                ok = ok + checkSize(minDim(j,:),DATObjects(i).size);
            end
            if(ok==0)
                listToRejectDAT = [listToRejectDAT i];
                continue;
            end
            if(DATObjects(i).DoT.value == -1)
                listToRejectDAT = [listToRejectDAT i];
                continue;
            end
        end
        for i=1:length(listToRejectGT)
            tmp = listToRejectGT(i);
            GTObjects = removeElement(GTObjects,listToRejectGT(i));
            listToRejectGT(listToRejectGT > tmp) = listToRejectGT(listToRejectGT > tmp)-1;
        end
        for i=1:length(listToRejectDAT)
            tmp = listToRejectDAT(i);
            DATObjects = removeElement(DATObjects,listToRejectDAT(i));
            listToRejectDAT(listToRejectDAT > tmp) = listToRejectDAT(listToRejectDAT > tmp)-1;
        end


        
        matchedDATIdx = [];
        nbOfExceptionNotMatched = 0;
        for i=1:length(GTObjects)           %Yeah, I know O(n^2)... it should be O(N!)
            
            exception = 0;
            if(isempty(GTObjects(i).LastDetection.objectType))
                exception = 1;
            end
                
            maxCommonArea = 0;
            maxIdx = -1;
            for j=1:length(DATObjects)
            %Struct miss/match/falseAlarm:
            %    {GTIdx, DATIdx, common area, overlapRatio
            %    common area ratio, scale ratio, weighted scale ratio, 
            %    hit rate}
                tmpCommonArea = calculateCommonArea(GTObjects(i),DATObjects(j));
                if(tmpCommonArea>maxCommonArea)
                    if(isempty(find(matchedDATIdx==j,1)))
                        maxIdx = j;
                        maxCommonArea = tmpCommonArea;
                    end
                end
            end
            if maxIdx == -1
                if(exception==0)
                    miss = struct('frame',k,'GTIdx',i,'DATIdx',-1,'GTRealIdx',GTObjects(i).ID,'DATRealIdx',-1,'commonArea',0,'commonAreaRatio',0,'overlapRatio',0,'scaleRatio',0,'weightedScaleRatio',0,'hitRate',0,'result','miss');
                    allMisses = [allMisses miss];
                    mappedObjects = checkIfThere(mappedObjects,str2num(GTObjects(i).ID),miss);
                    nbMisses = nbMisses + 1;

                    objectAssociations(str2num(GTObjects(i).ID)+1,maxObjectDATIdx+2) = objectAssociations(str2num(GTObjects(i).ID)+1,maxObjectDATIdx+2)+1; %miss
                else
                    nbOfExceptionNotMatched = nbOfExceptionNotMatched + 1;
                end
            else
                GTarea = calcArea(GTObjects(i));
                DATarea = calcArea(DATObjects(maxIdx));
                commonAreaRatio = min([maxCommonArea/GTarea maxCommonArea/DATarea]);
                overlapRatio = maxCommonArea/(GTarea + DATarea - maxCommonArea);
                scaleRatio = DATarea/GTarea;
                weightedScaleRatio = UAG(scaleRatio,sigma);
                hitRate = weightedScaleRatio*commonAreaRatio;
                if(overlapRatio > 0.2)                    
                    match = struct('frame',k,'GTIdx',i,'DATIdx',maxIdx,'GTRealIdx',GTObjects(i).ID,'DATRealIdx',DATObjects(maxIdx).ID,'commonArea',maxCommonArea,'commonAreaRatio',commonAreaRatio,'overlapRatio',overlapRatio,'scaleRatio',scaleRatio,'weightedScaleRatio',weightedScaleRatio,'hitRate',hitRate,'result','hit');
                    allMatches = [allMatches match];
                    mappedObjects = checkIfThere(mappedObjects,str2num(GTObjects(i).ID),match);
                    matchedDATIdx = [matchedDATIdx maxIdx];
                    nbMatches = nbMatches + 1;
                    
                    objectAssociations(str2num(GTObjects(i).ID)+1,str2num(DATObjects(maxIdx).ID)+1) = objectAssociations(str2num(GTObjects(i).ID)+1,str2num(DATObjects(maxIdx).ID)+1)+1; %hit
                else
                    if(exception==0)
                        miss = struct('frame',k,'GTIdx',i,'DATIdx',-1,'GTRealIdx',GTObjects(i).ID,'DATRealIdx',-1,'commonArea',0,'commonAreaRatio',0,'overlapRatio',0,'scaleRatio',0,'weightedScaleRatio',0,'hitRate',0,'result','miss');
                        allMisses = [allMisses miss];
                        mappedObjects = checkIfThere(mappedObjects,str2num(GTObjects(i).ID),miss);
                        nbMisses = nbMisses + 1;

                        objectAssociations(str2num(GTObjects(i).ID)+1,maxObjectDATIdx+2) = objectAssociations(str2num(GTObjects(i).ID)+1,maxObjectDATIdx+2)+1; %miss
                    else
                        nbOfExceptionNotMatched = nbOfExceptionNotMatched + 1;
                    end
                end
            end
        end
        if length(allMatches) ~= length(DATObjects)
        %Check if all DATObjects are associated. IF NOT
            for j=1:length(DATObjects)
                if checkIfMatched(allMatches,DATObjects(j)) == 1
                    continue;
                else
                    %[FalseAlarm]
                    falseAlarm = struct('frame',k,'GTIdx',-1,'DATIdx',j,'GTRealIdx',-1,'DATRealIdx',DATObjects(j).ID,'commonArea',0,'commonAreaRatio',0,'overlapRatio',0,'scaleRatio',0,'weightedScaleRatio',0,'hitRate',0,'result','false alarm');
                    allFalseAlarms = [allFalseAlarms falseAlarm];
                    %tmp = length(mappedObjects);
                    %mappedObjects = checkIfThere(mappedObjects,maxObjectGTIdx+offset,falseAlarm);
                    %if(tmp<length(mappedObjects))
                    %    offset = offset+1;
                    %end
                    nbFalseAlarms = nbFalseAlarms + 1;
                    
                    objectAssociations(maxObjectGTIdx+2,str2num(DATObjects(j).ID)+1) = objectAssociations(maxObjectGTIdx+2,str2num(DATObjects(j).ID)+1)+1; %miss
                end
            end
        end
    end
    frame = struct('allMatches',allMatches,'allMisses',allMisses,'allFalseAlarms',allFalseAlarms);
    allFrames = [allFrames frame];
    %Calculate FDA
    if isempty([DATObjects GTObjects])
        FDA(k+1) = 1;
        FDA_ST(k+1) = 1;
        FDA_BT(k+1) = 1;
    else
        for i=1:length(allMatches)
            FDA(k+1) = FDA(k+1)+allMatches(i).overlapRatio/((length(GTObjects)-nbOfExceptionNotMatched+length(DATObjects))/2);
            if(allMatches(i).overlapRatio>=FDATreshold)
            	FDA_ST(k+1) = FDA_ST(k+1)+1/((length(GTObjects)-nbOfExceptionNotMatched+length(DATObjects))/2);
                FDA_BT(k+1) = FDA_BT(k+1)+1/((length(GTObjects)-nbOfExceptionNotMatched+length(DATObjects))/2);
            else
                FDA_ST(k+1) = FDA_ST(k+1)+allMatches(i).overlapRatio/((length(GTObjects)-nbOfExceptionNotMatched+length(DATObjects))/2);
            end
        end
    end
    if(FDA(k+1)~=1)
        %FDA(k+1)
    end
end

%SFDA = 0;
allObjectNb = 0;
for k=1:NFrames+1
    allObjectNb = allObjectNb + length(allFrames(k).allMisses) + length(allFrames(k).allMatches) + length(allFrames(k).allFalseAlarms);
end
%for k=1:NFrames+1
%    allObjectNb = length(allFrames(k).allMisses) + length(allFrames(k).allMatches) + length(allFrames(k).allFalseAlarms);
%    SFDA = SFDA + FDA(k)/allObjectNb;
%end
SFDA = mean(FDA);
SFDA_ST = mean(FDA_ST);
SFDA_BT = mean(FDA_BT);

TDA = zeros(maxObjectGTIdx+1,1);
TDA_ST = zeros(maxObjectGTIdx+1,1);
TDA_BT = zeros(maxObjectGTIdx+1,1);
Stability = zeros(maxObjectGTIdx+1,1);
detectedObjects = [];

for i=1:length(mappedObjects)
    object = mappedObjects(i);
    objectIdx = object.objectIdx + 1;
    [value idx] = max(objectAssociations(objectIdx,1:maxObjectDATIdx+1));
    if(value > 0)
        for k=1:length(object.mamifa)
            if(object.mamifa(k).overlapRatio > 0.2)
                if(str2num(object.mamifa(k).DATRealIdx)+1 == idx)
                    TDA(objectIdx) = TDA(objectIdx) + object.mamifa(k).overlapRatio;
                    if(object.mamifa(k).overlapRatio >= TDAThreshold)
                        TDA_ST(objectIdx) = TDA_ST(objectIdx) + 1;
                        TDA_BT(objectIdx) = TDA_BT(objectIdx) + 1;
                    else
                        TDA_ST(objectIdx) = TDA_ST(objectIdx) + object.mamifa(k).overlapRatio;
                    end
                end
            end
        end
        %sum1 = sum(objectAssociations(i,1:size(objectAssociations,2)-1));
        %sum2 = sum(objectAssociations(1:size(objectAssociations,1)-1,idx));
        sum1 = sum(objectAssociations(objectIdx,:));
        sum2 = sum(objectAssociations(:,idx));
        
        tmp = (sum1 + sum2 - value);
        if(tmp>0)
            TDA(objectIdx) = TDA(objectIdx) / tmp;
            TDA_ST(objectIdx) = TDA_ST(objectIdx) / tmp;
            TDA_BT(objectIdx) = TDA_BT(objectIdx) / tmp;
            if(~modif)
                detectedObjects = [detectedObjects objectIdx];
            end
        end
        
        numberOfDifferentClass = length(find(objectAssociations(objectIdx,1:maxObjectDATIdx+1)~=0));
        Stability(objectIdx) = (length(object.mamifa)-(numberOfDifferentClass-1))/length(object.mamifa);
    end
    if(modif)
        detectedObjects = [detectedObjects objectIdx];
    end
end
%if(~modif)
Stability = Stability(detectedObjects);
TDA = TDA(detectedObjects);
TDA_ST = TDA_ST(detectedObjects);
TDA_BT = TDA_BT(detectedObjects);
%end

nD = 0;
for i=1:maxObjectDATIdx+1
   idx = find(objectAssociations(1:maxObjectGTIdx+1,i)>0);
   if(~isempty(idx))
       nD = nD+1;
   end
end

% for i=1:length(mappedObjects)
%     object = mappedObjects(i);
%     idx = -1;
%     associatedOnce = 0;
%     nbAssociations = zeros(1,maxObjectDATIdx);
%     for j=1:length(object.mamifa)
%         if(object.mamifa(j).DATIdx > 0)
%             nbAssociations(object.mamifa(j).DATIdx) = nbAssociations(object.mamifa(j).DATIdx)+1;
%             associatedOnce = 1;
%         end
%     end
%     if(associatedOnce)
%         [tmp idx] = max(nbAssociations);
%     end
%     if(idx > 0)
%         overlapRatioNonZero = 0;
%         for k=1:length(object.mamifa)
%             if(object.mamifa(k).overlapRatio > 0.2)
%                 if(object.mamifa(k).DATIdx == idx)
%                 	TDA(i) = TDA(i) + object.mamifa(k).overlapRatio;
%                     if(object.mamifa(k).overlapRatio >= TDAThreshold)
%                     	TDA_ST(i) = TDA_ST(i) + 1;
%                     	TDA_BT(i) = TDA_BT(i) + 1;
%                     else
%                         TDA_ST(i) = TDA_ST(i) + object.mamifa(k).overlapRatio;
%                     end
%                 end
%                 overlapRatioNonZero = overlapRatioNonZero+1;                   
%             end
%         end
%         if(overlapRatioNonZero > 0)
%             TDA(i) = TDA(i) / overlapRatioNonZero;
%             TDA_ST(i) = TDA_ST(i) / overlapRatioNonZero;
%             TDA_BT(i) = TDA_BT(i) / overlapRatioNonZero;
%             detectedObjects = [detectedObjects i];
%         end
%     end
% end
% TDA = TDA(detectedObjects);
% TDA_ST = TDA_ST(detectedObjects);
% TDA_BT = TDA_BT(detectedObjects);
meanStability = mean(Stability);

STDA = sum(TDA);
STDA_ST = sum(TDA_ST);
STDA_BT = sum(TDA_BT);

if(modif)
    %ATA = STDA / ((maxObjectGTIdx+nD+1)/2);
    %ATA_ST = STDA_ST / ((maxObjectGTIdx+nD+1)/2);
    %ATA_BT = STDA_BT / ((maxObjectGTIdx+nD+1)/2);
    ATA = STDA / ((maxObjectGTIdx+nD)/2);
    ATA_ST = STDA_ST / ((maxObjectGTIdx+nD)/2);
    ATA_BT = STDA_BT / ((maxObjectGTIdx+nD)/2);
else
    ATA = STDA / length(TDA);
    ATA_ST = STDA_ST / length(TDA);
    ATA_BT = STDA_BT / length(TDA);
end
if(verbose)
    %figure;plot(FDA);%title('FDA plot');
    figure;hist(FDA);%title('FDA histogram'); 
    xlabel('FDA');ylabel('Nombre de trames');
    %figure;plot(FDA_ST);%title('FDA Soft Treshold plot');
    figure;hist(FDA_ST);
    xlabel('FDA\_ST');ylabel('Nombre de trames');
    %figure;plot(FDA_BT);%title('FDA Binary Treshold plot');
    figure;hist(FDA_BT);
    xlabel('FDA\_BT');ylabel('Nombre de trames');
    figure;hist(TDA);
    xlabel('TDA');
    ylabel(['Nombre d' 39 'objets']);
    figure;hist(TDA_ST);
    xlabel('TDA\_ST');
    ylabel(['Nombre d' 39 'objets']);
    figure;hist(TDA_BT);
    xlabel('TDA\_BT');
    ylabel(['Nombre d' 39 'objets']);
    figure;hist(Stability);title('Stability');

    fprintf('SFDA: %5.5f\n',SFDA);
    fprintf('SFDA_ST: %5.5f\n',SFDA_ST);
    fprintf('SFDA_BT: %5.5f\n',SFDA_BT);
    fprintf('ATA: %5.5f\n',ATA);
    fprintf('ATA_ST: %5.5f\n',ATA_ST);
    fprintf('ATA_BT: %5.5f\n',ATA_BT);
    fprintf('Mean Stability: %5.5f\n',mean(Stability));
    fprintf('Nombre de DAT Objects: %5.5i\n',nD);
    fprintf('Nombre de GT Objects: %5.5i\n',length(mappedObjects));
    fprintf('Nombre de Matched Objects: %5.5i\n',length(TDA));
end
%TRACKING
%--------

%Step 1: Check if ID switch ? (it should be done)
end

function mappedObjects = checkIfThere(mappedObjects,idx,mamifa)
    falseAlarmTag = 0;
    if(mamifa.GTIdx == -1)
        falseAlarmTag = 1;
    end
    if(isempty(mappedObjects))  %only once
        object = struct('objectIdx',idx,'falseAlarmTag',falseAlarmTag,'mamifa',mamifa);
        mappedObjects = object;
    else
        tmp = findIdx(mappedObjects,idx);
        if(tmp==0)
            object = struct('objectIdx',idx,'falseAlarmTag',falseAlarmTag,'mamifa',mamifa);
            mappedObjects = [mappedObjects object];
        else
            mappedObjects(tmp).mamifa = [mappedObjects(tmp).mamifa mamifa];
        end
    end
end

function isThere = findIdx(mappedObjects,idx)
    isThere = 0;
    for i=1:length(mappedObjects)
        if(mappedObjects(i).objectIdx == idx)
            isThere = i;
            break;
        end
    end
end

function B = removeElement(A,idx)
    C = A;
    if(idx>1)
        C(1:idx-1) = A(1:idx-1);
    end
    if(idx<length(A))
        C(idx:length(A)-1) = A(idx+1:length(A));
    end
    B = C(1:length(A)-1);
end

function y = UAG(s,sigma)   %univariate assymetric gaussian (more or less)
    mu = 1;
   
    y = zeros(length(s),1);
    for i=1:length(s)
        if(s(i)>1)
        	y(i) = exp(-((s(i)-mu)/(2*sigma^2)));
        else
            y(i) = exp(-(1/s(i)-mu/(2*sigma^2)));    
        end
    end
end

function commonArea = calculateCommonArea(A,B)
    aVec = [A.position.x A.position.y A.size.width A.size.height];
    bVec = [B.position.x B.position.y B.size.width B.size.height];

    commonArea = rectint(aVec,bVec);
end

function itMatched = checkIfMatched(allMatches,DATObject)
    itMatched = 0;
    for i=1:length(allMatches)
        if(allMatches(i).DATIdx == str2num(DATObject.ID))
            itMatched = 1;
            break;
        end
    end
end

function area = calcArea(A)
    area = A.size.width*A.size.height;
end

function ok = checkSize(minDim,rect)
    if (rect.width>minDim(1) && rect.height>minDim(2))
        ok = 1;
    else
        ok = 0;
    end
end

function objects = lookupChild(childNode)
    objects = [];
    
    while ~isempty(childNode)
        if childNode.getNodeType == childNode.ELEMENT_NODE
            objectStruct = makeStructFromNode(childNode);
            object.ID = objectStruct.Attributes.Value;
            
            for i=1:length(objectStruct.Children)
                objectStructChildNode = objectStruct.Children(i);
                switch(objectStructChildNode.Name)
                case 'Position';
                    switch(objectStructChildNode.Attributes(1).Name)
                        case 'x';
                            object.position.x = str2num(objectStructChildNode.Attributes(1).Value);
                            object.position.y = str2num(objectStructChildNode.Attributes(2).Value);
                        case 'y';
                            object.position.x = str2num(objectStructChildNode.Attributes(2).Value);
                            object.position.y = str2num(objectStructChildNode.Attributes(1).Value);                            
                    end
                case 'Size';
                    switch(objectStructChildNode.Attributes(1).Name)
                        case 'height';
                            object.size.height = str2num(objectStructChildNode.Attributes(1).Value);
                            object.size.width = str2num(objectStructChildNode.Attributes(2).Value);
                        case 'width';
                            object.size.height = str2num(objectStructChildNode.Attributes(2).Value);
                            object.size.width = str2num(objectStructChildNode.Attributes(1).Value);
                    end
                case 'DoT';
                    object.DoT.value = str2num(objectStructChildNode.Attributes.Value);
                case 'LastDetection';
                    for j=1:length(objectStructChildNode.Attributes)
                        tmpName = objectStructChildNode.Attributes(j).Name;
                        switch(tmpName)
                            case 'object'
                                object.LastDetection.objectType = objectStructChildNode.Attributes(j).Value;
                            case 'frame'
                                object.LastDetection.frame = str2num(objectStructChildNode.Attributes(j).Value);
                            case 'generalConfidence'
                                
                            case 'confidence'
                        end
                    end
                end
            end
            objects = [objects object];
        end
        childNode = childNode.getNextSibling;
    end    
end
